home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 1 / QRZ Ham Radio Callsign Database - December 1993.iso / ucsd / packet / tcpip / amiga / asrc29k.lha / popserv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-08  |  13.3 KB  |  684 lines

  1. /* POP Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in popcli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <time.h>
  11. #ifdef AMIGA
  12. #include "amiga/stat.h"
  13. #else
  14. #include <sys/stat.h>
  15. #endif
  16. #ifdef UNIX
  17. #include <sys/types.h>
  18. #endif
  19. #if    defined(__STDC__) || defined(__TURBOC__)
  20. #include <stdarg.h>
  21. #endif
  22. #include <ctype.h>
  23. #include <setjmp.h>
  24. #include "global.h"
  25. #include "mbuf.h"
  26. #include "cmdparse.h"
  27. #include "socket.h"
  28. #include "proc.h"
  29. #include "files.h"
  30. #include "pop.h"
  31. #include "commands.h"
  32.  
  33. extern char Nospace[];
  34.  
  35. static struct pop_scb *create_scb __ARGS((void));
  36. static void delete_scb __ARGS((struct pop_scb *scb));
  37. static void popserv __ARGS((int s,void *unused,void *p));
  38. static int poplogin __ARGS((char *pass,char *username));
  39. void pop_sm __ARGS((struct pop_scb *scb));
  40. void rrip __ARGS((register char *s));
  41.  
  42. static int Spop = -1; /* prototype socket for service */
  43.  
  44. /* Start up POP receiver service */
  45. int pop1(argc,argv,p)
  46. int argc;
  47. char *argv[];
  48. void *p;
  49. {
  50.     struct sockaddr_in lsocket;
  51.     int s;
  52.  
  53.     if (Spop != -1) {
  54.         return 0;
  55.     }
  56.  
  57.     psignal(Curproc,0);        /* Don't keep the parser waiting */
  58.     chname(Curproc,"POP listener");
  59.  
  60.     lsocket.sin_family = AF_INET;
  61.     lsocket.sin_addr.s_addr = INADDR_ANY;
  62.     if(argc < 2)
  63.         lsocket.sin_port = IPPORT_POP;
  64.     else
  65.         lsocket.sin_port = atoi(argv[1]);
  66.  
  67.     Spop = socket(AF_INET,SOCK_STREAM,0);
  68.  
  69.     bind(Spop,(char *)&lsocket,sizeof(lsocket));
  70.  
  71.     listen(Spop,1);
  72.  
  73.     for (;;) {
  74.         if((s = accept(Spop,NULLCHAR,(int *)NULL)) == -1)
  75.             break;    /* Service is shutting down */
  76.  
  77.         /* Spawn a server */
  78.  
  79.         newproc("POP server",2048,popserv,s,NULL,NULL);
  80.     }
  81.     return 0;
  82. }
  83.  
  84. /* Shutdown POP service (existing connections are allowed to finish) */
  85. int pop0(argc,argv,p)
  86. int argc;
  87. char *argv[];
  88. void *p;
  89. {
  90.     close_s(Spop);
  91.     Spop = -1;
  92.     return 0;
  93. }
  94.  
  95. static void popserv(s,unused,p)
  96. int s;
  97. void *unused;
  98. void *p;
  99. {
  100.     struct pop_scb *scb;
  101.  
  102.     sockowner(s,Curproc);        /* We own it now */
  103.     mainlog(s,"open POP");
  104.  
  105.     if((scb = create_scb()) == NULLSCB) {
  106.         tprintf(Nospace);
  107.         mainlog(s,"close POP - no space");
  108.         close_s(s);
  109.         return;
  110.     }
  111.  
  112.     scb->socket = s;
  113.     scb->state  = AUTH;
  114.  
  115.     (void) usprintf(s,greeting_msg,Hostname);
  116.  
  117. loop:    if ((scb->count = recvline(s,scb->buf,BUF_LEN)) == -1){
  118.         /* He closed on us */
  119.  
  120.         goto quit;
  121.     }
  122.  
  123.     rip(scb->buf);
  124.     if (strlen(scb->buf) == 0)        /* Ignore blank cmd lines */
  125.         goto loop;
  126.     pop_sm(scb);
  127.     if (scb->state == DONE)
  128.         goto quit;
  129.  
  130.     goto loop;
  131.  
  132. quit:
  133.     mainlog(scb->socket,"close POP");
  134.     close_s(scb->socket);
  135.     delete_scb(scb);
  136. }
  137.  
  138.  
  139. /* Create control block, initialize */
  140.  
  141. static struct pop_scb *create_scb()
  142. {
  143.     register struct pop_scb *scb;
  144.  
  145.     if((scb = (struct pop_scb *)callocw(1,sizeof (struct pop_scb))) == NULLSCB)
  146.         return NULLSCB;
  147.  
  148.     scb->username[0] = '\0';
  149.     scb->msg_status = NULL;
  150.     scb->wf = NULL;
  151.  
  152.     scb->count = scb->folder_file_size = scb->msg_num = 0;
  153.  
  154.     scb->folder_modified = FALSE;
  155.     return scb;
  156. }
  157.  
  158.  
  159. /* Free resources, delete control block */
  160.  
  161. static void delete_scb(scb)
  162. register struct pop_scb *scb;
  163. {
  164.  
  165.     if (scb == NULLSCB)
  166.         return;
  167.     if (scb->wf != NULL)
  168.         fclose(scb->wf);
  169.     if (scb->msg_status  != NULL)
  170.         free((char *)scb->msg_status);
  171.  
  172.     free((char *)scb);
  173. }
  174.  
  175. /* replace terminating end of line marker(s) (\r and \n) with null */
  176. void rrip(s)
  177. register char *s;
  178. {
  179.     register char *cp;
  180.  
  181.     if((cp = strchr(s,'\r')) != NULLCHAR)
  182.         *cp = '\0';
  183.     if((cp = strchr(s,'\n')) != NULLCHAR)
  184.         *cp = '\0';
  185. }
  186.  
  187. /* --------------------- start of POP server code ------------------------ */
  188.  
  189. #define    BITS_PER_WORD        16
  190.  
  191. #define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  192.  
  193. /* Command string specifications */
  194.  
  195. static char    ackd_cmd[] = "ACKD",
  196.         acks_cmd[] = "ACKS",
  197. #ifdef POP_FOLDERS
  198.         fold_cmd[] = "FOLD ",
  199. #endif
  200.         login_cmd[] = "HELO ",
  201.         nack_cmd[] = "NACK",
  202.         quit_cmd[] = "QUIT",
  203.         read_cmd[] = "READ",
  204.         retr_cmd[] = "RETR";
  205.  
  206. void pop_sm(scb)
  207. struct pop_scb *scb;
  208. {
  209.     char password[40];
  210.     void state_error(struct pop_scb *,char *);
  211.     void open_folder(struct pop_scb *);
  212.     void do_cleanup(struct pop_scb *);
  213.     void read_message(struct pop_scb *);
  214.     void retrieve_message(struct pop_scb *);
  215.     void deletemsg(struct pop_scb *,int);
  216.     void get_message(struct pop_scb *,int);
  217.     void print_message_length(struct pop_scb *);
  218.     void close_folder(struct pop_scb *);
  219. #ifdef POP_FOLDERS
  220.     void select_folder(struct pop_scb *);
  221. #endif
  222.  
  223.  
  224.     switch(scb->state) {
  225.     case AUTH:
  226.         if (strncmp(scb->buf,login_cmd,strlen(login_cmd)) == 0){
  227.             sscanf(scb->buf,"HELO %s%s",scb->username,password);
  228.  
  229.             if (!poplogin(scb->username,password)) {
  230.                 mainlog(scb->socket,"POP access DENIED to %s",
  231.                         scb->username);
  232.                 state_error(scb,"Access DENIED!!");
  233.                 return;
  234.             }
  235.  
  236.             mainlog(scb->socket,"POP access granted to %s",
  237.                     scb->username);
  238.             open_folder(scb);
  239.         } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  240.             do_cleanup(scb);
  241.         } else
  242.             state_error(scb,"(AUTH) Expected HELO or QUIT command");
  243.         break;
  244.  
  245.     case MBOX:
  246.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  247.             read_message(scb);
  248.  
  249. #ifdef POP_FOLDERS
  250.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  251.             select_folder(scb);
  252.  
  253. #endif
  254.  
  255.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  256.             do_cleanup(scb);
  257.         } else
  258.             state_error(scb,
  259. #ifdef POP_FOLDERS
  260.                     "(MBOX) Expected FOLD, READ, or QUIT command");
  261. #else
  262.                     "(MBOX) Expected READ or QUIT command");
  263. #endif
  264.         break;
  265.  
  266.     case ITEM:
  267.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  268.             read_message(scb);
  269.  
  270. #ifdef POP_FOLDERS
  271.  
  272.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  273.             select_folder(scb);
  274. #endif
  275.  
  276.         else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  277.             retrieve_message(scb);
  278.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  279.             do_cleanup(scb);
  280.         else
  281.             state_error(scb,
  282. #ifdef POP_FOLDERS
  283.                "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  284. #else
  285.                "(ITEM) Expected READ, RETR, or QUIT command");
  286. #endif
  287.         break;
  288.  
  289.     case NEXT:
  290.         if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  291.                 /* ACKD processing */
  292.             deletemsg(scb,scb->msg_num);
  293.             scb->msg_num++;
  294.             get_message(scb,scb->msg_num);
  295.         } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  296.                 /* ACKS processing */
  297.             scb->msg_num++;
  298.             get_message(scb,scb->msg_num);
  299.         } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  300.                 /* NACK processing */
  301.             fseek(scb->wf,scb->curpos,0);
  302.         } else {
  303.             state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  304.             return;
  305.         }
  306.  
  307.         print_message_length(scb);
  308.         scb->state  = ITEM;
  309.         break;
  310.  
  311.     case DONE:
  312.         do_cleanup(scb);
  313.         break;
  314.  
  315.     default:
  316.         state_error(scb,"(TOP) State Error!!");
  317.         break;
  318.     }
  319. }
  320.  
  321. void do_cleanup(scb)
  322. struct pop_scb *scb;
  323. {
  324.     close_folder(scb);
  325.     (void) usprintf(scb->socket,signoff_msg);
  326.     scb->state = DONE;
  327. }
  328.  
  329. void state_error(scb,msg)
  330. struct pop_scb *scb;
  331. char *msg;
  332. {
  333.     (void) usprintf(scb->socket,error_rsp,msg);
  334.     scb->state = DONE;
  335. }
  336.  
  337. #ifdef POP_FOLDERS
  338.  
  339. select_folder(scb)
  340. struct pop_scb    *scb;
  341. {
  342.     sscanf(scb->buf,"FOLD %s",scb->username);
  343.  
  344.     if (scb->wf != NULL)
  345.         close_folder(scb);
  346.  
  347.     open_folder(scb);
  348. }
  349.  
  350. #endif
  351.  
  352.  
  353. void close_folder(scb)
  354. struct pop_scb *scb;
  355. {
  356.     char folder_pathname[64];
  357.     char line[BUF_LEN];
  358.     FILE *fd;
  359.     int deleted = FALSE;
  360.     int msg_no = 0;
  361.     struct stat folder_stat;
  362.     int newmail(struct pop_scb *);
  363.     int isdeleted(struct pop_scb *,int);
  364.  
  365.     if (scb->wf == NULL)
  366.         return;
  367.  
  368.     if (!scb->folder_modified) {
  369.         /* no need to re-write the folder if we have not modified it */
  370.  
  371.         fclose(scb->wf);
  372.         scb->wf = NULL;
  373.  
  374.         free((char *)scb->msg_status);
  375.         scb->msg_status = NULL;
  376.         return;
  377.     }
  378.  
  379.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  380.  
  381.     if (newmail(scb)) {
  382.         /* copy new mail into the work file and save the
  383.            message count for later */
  384.  
  385.         if ((fd = fopen(folder_pathname,"r")) == NULL) {
  386.             state_error(scb,"Unable to add new mail to folder");
  387.             return;
  388.         }
  389.  
  390.         fseek(scb->wf,0,2);
  391.         fseek(fd,scb->folder_file_size,0);
  392.         while (!feof(fd)) {
  393.             fgets(line,BUF_LEN,fd);
  394.             fputs(line,scb->wf);
  395.         }
  396.  
  397.         fclose(fd);
  398.     }
  399.  
  400.     /* now create the updated mail folder */
  401.  
  402.     if ((fd = fopen(folder_pathname,"w")) == NULL){
  403.         state_error(scb,"Unable to update mail folder");
  404.         return;
  405.     }
  406.  
  407.     rewind(scb->wf);
  408.     while (!feof(scb->wf)){
  409.         fgets(line,BUF_LEN,scb->wf);
  410.  
  411.         if (isSOM(line)){
  412.             msg_no++;
  413.             if (msg_no <= scb->folder_len)
  414.                 deleted = isdeleted(scb,msg_no);
  415.             else
  416.                 deleted = FALSE;
  417.         }
  418.  
  419.         if (deleted)
  420.             continue;
  421.  
  422.         fputs(line,fd);
  423.     }
  424.  
  425.     fclose(fd);
  426.  
  427.     /* trash the updated mail folder if it is empty */
  428.  
  429.     if ((stat(folder_pathname,&folder_stat) == 0) && (folder_stat.st_size == 0))
  430.         unlink(folder_pathname);
  431.  
  432.     fclose(scb->wf);
  433.     scb->wf = NULL;
  434.  
  435.     free((char *)scb->msg_status);
  436.     scb->msg_status = NULL;
  437. }
  438.  
  439. void open_folder(scb)
  440. struct pop_scb    *scb;
  441. {
  442.     char folder_pathname[64];
  443.     char line[BUF_LEN];
  444.     FILE *fd;
  445.     FILE *tmpfile();
  446.     struct stat folder_stat;
  447.  
  448.  
  449.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  450.     scb->folder_len       = 0;
  451.     scb->folder_file_size = 0;
  452.     if (stat(folder_pathname,&folder_stat)){
  453.          (void) usprintf(scb->socket,no_mail_rsp);
  454.          return;
  455.     }
  456.  
  457.     scb->folder_file_size = folder_stat.st_size;
  458.     if ((fd = fopen(folder_pathname,"r")) == NULL){
  459.         state_error(scb,"Unable to open mail folder");
  460.         return;
  461.     }
  462.  
  463.     if ((scb->wf = tmpfile()) == NULL) {
  464.         state_error(scb,"Unable to create work folder");
  465.         return;
  466.     }
  467.  
  468.     while(!feof(fd)) {
  469.         fgets(line,BUF_LEN,fd);
  470.  
  471.         /* scan for begining of a message */
  472.  
  473.         if (isSOM(line))
  474.             scb->folder_len++;
  475.  
  476.         /* now put  the line in the work file */
  477.  
  478.         fputs(line,scb->wf);
  479.     }
  480.  
  481.     fclose(fd);
  482.  
  483.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  484.  
  485.     if ((((scb->folder_len) % BITS_PER_WORD) != 0) ||
  486.         (scb->msg_status_size == 0))
  487.         scb->msg_status_size++;
  488.  
  489.     if ((scb->msg_status = (unsigned int *) callocw(scb->msg_status_size,
  490.                 sizeof(unsigned int))) == NULL) {
  491.         state_error(scb,"Unable to create message status array");
  492.         return;
  493.     }
  494.  
  495.     (void) usprintf(scb->socket,count_rsp,scb->folder_len);
  496.  
  497.     scb->state  = MBOX;
  498. }
  499.  
  500. void read_message(scb)
  501. struct pop_scb    *scb;
  502. {
  503.     if (scb->buf[sizeof(read_cmd) - 1] == ' ')
  504.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  505.     else
  506.         scb->msg_num++;
  507.  
  508.     get_message(scb,scb->msg_num);
  509.     print_message_length(scb);
  510.     scb->state  = ITEM;
  511. }
  512.  
  513. void retrieve_message(scb)
  514. struct pop_scb    *scb;
  515. {
  516.     char line[BUF_LEN];
  517.     long cnt;
  518.  
  519.     if (scb->msg_len == 0) {
  520.         state_error(scb,"Attempt to access a DELETED message!");
  521.         return;
  522.     }
  523.  
  524.     cnt  = scb->msg_len;
  525.     while(!feof(scb->wf) && (cnt > 0)) {
  526.         fgets(line,BUF_LEN,scb->wf);
  527.         rrip(line);
  528.  
  529.         (void) usprintf(scb->socket,msg_line,line);
  530.         cnt -= (strlen(line)+2);    /* Compensate for CRLF */
  531.     }
  532.  
  533.     scb->state = NEXT;
  534. }
  535.  
  536. void get_message(scb,msg_no)
  537. struct pop_scb    *scb;
  538. int msg_no;
  539. {
  540.     char line[BUF_LEN];
  541.     long ftell();
  542.  
  543.     scb->msg_len = 0;
  544.     if (msg_no > scb->folder_len) {
  545.         scb->curpos  = 0;
  546.         scb->nextpos = 0;
  547.         return;
  548.     } else {
  549.         /* find the message and its length */
  550.  
  551.         rewind(scb->wf);
  552.         while (!feof(scb->wf) && (msg_no > -1)) {
  553.             if (msg_no > 0)
  554.                 scb->curpos = ftell(scb->wf);
  555.             
  556.             fgets(line,BUF_LEN,scb->wf);
  557.             rrip(line);
  558.  
  559.             if (isSOM(line))
  560.                 msg_no--;
  561.  
  562.             if (msg_no != 0)
  563.                 continue;
  564.  
  565.             scb->nextpos  = ftell(scb->wf);
  566.             scb->msg_len += (strlen(line)+2);    /* Add CRLF */
  567.         }
  568.     }
  569.  
  570.     if (scb->msg_len > 0)
  571.         fseek(scb->wf,scb->curpos,0);
  572.  
  573.     /* we need the pointers even if the message was deleted */
  574.  
  575.     if  (isdeleted(scb,scb->msg_num))
  576.         scb->msg_len = 0;
  577. }
  578.  
  579. static int poplogin(username,pass)
  580. char *pass;
  581. char *username;
  582. {
  583.     char buf[80];
  584.     char *cp;
  585.     char *cp1;
  586.     FILE *fp;
  587.  
  588.     if((fp = fopen(Popusers,"r")) == NULLFILE) {
  589.         /* User file doesn't exist */
  590.         tprintf("POP users file %s not found\n",Popusers);
  591.         return(FALSE);
  592.     }
  593.  
  594.     while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
  595.         if(buf[0] == '#')
  596.             continue;    /* Comment */
  597.  
  598.         if((cp = strchr(buf,':')) == NULLCHAR)
  599.             /* Bogus entry */
  600.             continue;
  601.  
  602.         *cp++ = '\0';        /* Now points to password */
  603.         if(strcmp(username,buf) == 0)
  604.             break;        /* Found user name */
  605.     }
  606.  
  607.     if(feof(fp)) {
  608.         /* User name not found in file */
  609.  
  610.         fclose(fp);
  611.         return(FALSE);
  612.     }
  613.     fclose(fp);
  614.  
  615.     if ((cp1 = strchr(cp,':')) == NULLCHAR)
  616.         return(FALSE);
  617.  
  618.     *cp1 = '\0';
  619.     if(strcmp(cp,pass) != 0) {
  620.         /* Password required, but wrong one given */
  621.  
  622.         return(FALSE);
  623.     }
  624.  
  625.     /* whew! finally made it!! */
  626.  
  627.     return(TRUE);
  628. }
  629.  
  630. int isdeleted(scb,msg_no)
  631. struct pop_scb *scb;
  632. int msg_no;
  633. {
  634.     unsigned int mask = 1,offset;
  635.  
  636.     msg_no--;
  637.     offset = msg_no / BITS_PER_WORD;
  638.     mask <<= msg_no % BITS_PER_WORD;
  639.     return (((scb->msg_status[offset]) & mask)? TRUE:FALSE);
  640. }
  641.  
  642. void deletemsg(scb,msg_no)
  643. struct pop_scb *scb;
  644. int msg_no;
  645. {
  646.     unsigned int mask = 1,offset;
  647.  
  648.     msg_no--;
  649.     offset = msg_no / BITS_PER_WORD;
  650.     mask <<= msg_no % BITS_PER_WORD;
  651.     scb->msg_status[offset] |= mask;
  652.     scb->folder_modified = TRUE;
  653. }
  654.  
  655. int newmail(scb)
  656. struct pop_scb *scb;
  657. {
  658.     char folder_pathname[64];
  659.     struct stat folder_stat;
  660.  
  661.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  662.  
  663.     if (stat(folder_pathname,&folder_stat)) {
  664.         state_error(scb,"Unable to get old mail folder's status");
  665.         return(FALSE);
  666.     } else
  667.         return ((folder_stat.st_size > scb->folder_file_size)? TRUE:FALSE);
  668. }
  669.  
  670. void print_message_length(scb)
  671. struct pop_scb *scb;
  672. {
  673.     char *print_control_string;
  674.  
  675.     if (scb->msg_len > 0)
  676.         print_control_string = length_rsp;
  677.     else if (scb->msg_num <= scb->folder_len)
  678.         print_control_string = length_rsp;
  679.     else
  680.         print_control_string = no_more_rsp;
  681.  
  682.     (void)usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
  683. }
  684.